/**
 * Copyright 2010 Terrestrial Ecosystem Research Network, licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or
 * agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */

package au.org.tern.ecoinformatics.oai.provider.sample.crosswalk;

import java.io.StringWriter;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import au.org.tern.ecoinformatics.oai.provider.crosswalk.Crosswalk;
import au.org.tern.ecoinformatics.oai.provider.sample.model.SampleDataset;
import au.org.tern.ecoinformatics.oai.provider.sample.model.SampleSpecies;
import au.org.tern.ecoinformatics.oai.provider.sample.model.SampleSpecies.RankValue;
import au.org.tern.ecoinformatics.oai.provider.util.DateUtils;
import eml.ecoinformatics_org.coverage_2_1.Coverage;
import eml.ecoinformatics_org.coverage_2_1.GeographicCoverage;
import eml.ecoinformatics_org.coverage_2_1.SingleDateTimeType;
import eml.ecoinformatics_org.coverage_2_1.TaxonomicClassificationType;
import eml.ecoinformatics_org.coverage_2_1.TaxonomicCoverage;
import eml.ecoinformatics_org.coverage_2_1.TemporalCoverage;
import eml.ecoinformatics_org.coverage_2_1.GeographicCoverage.BoundingCoordinates;
import eml.ecoinformatics_org.coverage_2_1.TemporalCoverage.RangeOfDates;
import eml.ecoinformatics_org.dataset_2_1.DatasetType;
import eml.ecoinformatics_org.eml_2_1.Eml;
import eml.ecoinformatics_org.party_2_1.ObjectFactory;
import eml.ecoinformatics_org.party_2_1.ResponsibleParty;
import eml.ecoinformatics_org.text_2_1.ParagraphType;
import eml.ecoinformatics_org.text_2_1.TextType;

/**
 * Shows a simple example of mapping custom domain object to generated Eml model.
 * 
 * Note: The Eml java model is automatically created from the EML XSD files, using JAXB.
 *       This can result in some strange looking code for constructing the model, so hopefully this example shows 
 *       some of the difficult aspects of that.
 *        
 * @author Vaughan Hobbs
 *
 */
public class SampleCrosswalk implements Crosswalk {

	private String identifierPrefix;
	
	public void setIdentifierPrefix(String identifierPrefix) {
		this.identifierPrefix = identifierPrefix;
	}

	@Override
	public Eml crosswalk(Object nativeObject) {
		try {
			Eml eml = new Eml();
			SampleDataset dataset = (SampleDataset) nativeObject;
			DatasetType emlDataset = new DatasetType();
			emlDataset.setAbstract(createTextType(dataset.getAbstrakt()));
			emlDataset.setShortName(dataset.getTitle());
			emlDataset.setPurpose(createTextType(dataset.getPurpose()));
			emlDataset.setCoverage(createCoverage(dataset));
			emlDataset.getCreator().add(createCreator(dataset));
			emlDataset.setPublisher(createPublisher(dataset));
			
			eml.setDataset(emlDataset);
			
			return eml;
		}
		catch (ClassCastException e) {
			return null;
		}
	}

	private ResponsibleParty createCreator(SampleDataset dataset) {
		ResponsibleParty party = new ResponsibleParty();
		ObjectFactory of = new ObjectFactory();
		JAXBElement<String> name = of.createResponsiblePartyOrganizationName(dataset.getCreatorName());
		party.getIndividualNameOrOrganizationNameOrPositionName().add(name);
		party.getElectronicMailAddress().add(dataset.getCreatorEmail());
		
		return party;
	}

	private ResponsibleParty createPublisher(SampleDataset dataset) {
		ResponsibleParty party = new ResponsibleParty();
		ObjectFactory of = new ObjectFactory();
		JAXBElement<String> name = of.createResponsiblePartyOrganizationName(dataset.getPublisherName());
		party.getIndividualNameOrOrganizationNameOrPositionName().add(name);
		party.getElectronicMailAddress().add(dataset.getPublisherEmail());
		
		return party;
	}

	private Coverage createCoverage(SampleDataset dataset) {
		Coverage coverage = new Coverage();
		GeographicCoverage geoCvg = extractGeographicCoverage(dataset);
		if (geoCvg != null) {
			coverage.getGeographicCoverageOrTemporalCoverageOrTaxonomicCoverage().add(geoCvg);
		}
		TemporalCoverage temporalCvg = extractTemporalCoverage(dataset);
		if (temporalCvg != null) {
			coverage.getGeographicCoverageOrTemporalCoverageOrTaxonomicCoverage().add(temporalCvg);
		}
		TaxonomicCoverage taxonomicCoverage = extractTaxonomicCoverage(dataset);
		if (taxonomicCoverage != null) {			
			coverage.getGeographicCoverageOrTemporalCoverageOrTaxonomicCoverage().add(taxonomicCoverage);
		}
		
		if (coverage.getGeographicCoverageOrTemporalCoverageOrTaxonomicCoverage() == null || coverage.getGeographicCoverageOrTemporalCoverageOrTaxonomicCoverage().size() == 0) {
			return null;
		}
		else {
			return coverage;
		}	
	}

	
	private TaxonomicCoverage extractTaxonomicCoverage(SampleDataset dataset) {
		eml.ecoinformatics_org.coverage_2_1.ObjectFactory of = new eml.ecoinformatics_org.coverage_2_1.ObjectFactory();
		TaxonomicCoverage taxonomicCoverage = of.createCoverageTaxonomicCoverage();
		taxonomicCoverage.setGeneralTaxonomicCoverage("Sample taxonomy");
		if (dataset.getSpeciesList() != null) {
			for (SampleSpecies species : dataset.getSpeciesList()) {
				taxonomicCoverage.getTaxonomicClassification().add(createTaxonomicClassification(species));
			}
		}
		if (taxonomicCoverage.getTaxonomicClassification().size() == 0) {
			return null;
		}
		else {
			return taxonomicCoverage;
		}
	}


	private TaxonomicClassificationType createTaxonomicClassification(SampleSpecies species) {
		TaxonomicClassificationType taxonElement = new TaxonomicClassificationType();
		taxonElement.getTaxonomicClassification().add(createClassification(species.getTaxonPath(), species));
		taxonElement.setTaxonRankName(species.getTaxonPath().get(0).getRank().toString());
		taxonElement.setTaxonRankValue(species.getTaxonPath().get(0).getValue());
		
		return taxonElement;
	}


	private TaxonomicClassificationType createClassification(List<RankValue> taxonPath, SampleSpecies species) {

		TaxonomicClassificationType taxonElement = new TaxonomicClassificationType();
		if (taxonPath != null && taxonPath.size() > 0) {
			taxonElement.setTaxonRankName(taxonPath.get(0).getRank().toString());
			taxonElement.setTaxonRankValue(taxonPath.get(0).getValue());

			// recursive
			if (taxonPath.size() > 1) {
				taxonElement.getTaxonomicClassification().add(
						createClassification(taxonPath.subList(1, taxonPath.size()), species));
			} else {
				// add common name
				taxonElement.getCommonName().add(species.getCommonName());
			}
		}
		
		return taxonElement;
	}

	private TemporalCoverage extractTemporalCoverage(SampleDataset dataset) {
		
		eml.ecoinformatics_org.coverage_2_1.ObjectFactory of 
			= new eml.ecoinformatics_org.coverage_2_1.ObjectFactory();
		SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");

		TemporalCoverage temporalCoverage = of.createTemporalCoverage();
		RangeOfDates dates = new RangeOfDates();
		SingleDateTimeType startDateTime = new SingleDateTimeType();
		SingleDateTimeType endDateTime = new SingleDateTimeType();

		Calendar calendar = Calendar.getInstance();
		calendar.setTime(dataset.getStartDate());
		startDateTime.setCalendarDate(dateFormatter.format(calendar.getTime()));
		calendar.setTime(dataset.getEndDate());
		endDateTime.setCalendarDate(dateFormatter.format(calendar.getTime()));
		
		dates.setBeginDate(startDateTime);
		dates.setEndDate(endDateTime);
		temporalCoverage.setRangeOfDates(dates);
		
		return temporalCoverage;
	}

	
	private GeographicCoverage extractGeographicCoverage(SampleDataset dataset) {
		// Create bounding box
		BoundingCoordinates boundingCoords = new BoundingCoordinates();
		boundingCoords.setEastBoundingCoordinate(BigDecimal.valueOf(dataset.getEasternExtent()));
		boundingCoords.setWestBoundingCoordinate(BigDecimal.valueOf(dataset.getWesternExtent()));
		boundingCoords.setNorthBoundingCoordinate(BigDecimal.valueOf(dataset.getNorthernExtent()));
		boundingCoords.setSouthBoundingCoordinate(BigDecimal.valueOf(dataset.getSouthernExtent()));
		
		GeographicCoverage geoCoverage = new GeographicCoverage();
		geoCoverage.setBoundingCoordinates(boundingCoords);
		
		return geoCoverage;
	}

	
	private TextType createTextType(String text) {
		TextType textType = new TextType();
		ParagraphType para = new ParagraphType();
		para.getContent().add(text);
		textType.getContent().addAll(para.getContent());
		
		return textType;	
	}

	
	@Override
	public String crosswalkToString(Object nativeObject) {
		Eml eml = crosswalk(nativeObject);
		try {
			JAXBContext jaxbContext = JAXBContext.newInstance("eml.ecoinformatics_org.eml_2_1");
			Marshaller marshaller = jaxbContext.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
			StringWriter writer = new StringWriter();
			marshaller.marshal(eml, writer);
			
			return "<metadata>" + writer.toString() + "</metadata>";
		} 
		catch (JAXBException je) {
			
			return null;			
		}
	}

	
	@Override
	public String getDatestamp(Object nativeItem) {

		return DateUtils.formatDate(((SampleDataset)nativeItem).getModificationDate(), false);
	}

}
